#include "gd32l23x.h"
#include "stdio.h"
#include "stdint.h"
#include "DevicesTime.h"
#include "DevicesCRC.h"
#include "DevicesRTC.h"


TimeType st_typeTimeDef = {
    .year = 2025,
    .month = 1,
    .day = 1,
    .hour = 0,
    .minute = 0,
    .second = 0,
    .week = 3,
};


void vRtcInit(void)
{
    /* enable PMU and BKP clock */
    rcu_periph_clock_enable(RCU_PMU);
    rcu_periph_clock_enable(RCU_BKP);
    /* enable the access of the RTC registers */
    pmu_backup_write_enable();

    rcu_rtc_clock_config(RCU_RTCSRC_IRC32K);

    rcu_periph_clock_enable(RCU_RTC);
    rtc_register_sync_wait();

    if(cRtcBkpCheck() != 0)
    {
        cRtcTimeSet(&st_typeTimeDef);

        cRtcBkpWrite(0, 0xBE);
        cRtcBkpSave();

        printf("cRtcBkpCheck fail.\r\n");
    }
}

int8_t cRtcTimeSet(TimeType *ptypeTime)
{
    rtc_parameter_struct rtc_initpara;
    uint32_t year = ptypeTime->year % 100;

    rtc_initpara.year           = ((year              / 10) * 16) + (year              % 10);
    rtc_initpara.month          = ((ptypeTime->month  / 10) * 16) + (ptypeTime->month  % 10);
    rtc_initpara.date           = ((ptypeTime->day    / 10) * 16) + (ptypeTime->day    % 10);
    rtc_initpara.hour           = ((ptypeTime->hour   / 10) * 16) + (ptypeTime->hour   % 10);
    rtc_initpara.minute         = ((ptypeTime->minute / 10) * 16) + (ptypeTime->minute % 10);
    rtc_initpara.second         = ((ptypeTime->second / 10) * 16) + (ptypeTime->second % 10);
    rtc_initpara.day_of_week    = ptypeTime->week;
    rtc_initpara.display_format = RTC_24HOUR;
    rtc_initpara.am_pm          = RTC_AM;
    rtc_initpara.factor_asyn    = 32 - 1;
    rtc_initpara.factor_syn     = 1000 - 1;
    rtc_init(&rtc_initpara);

    return 0;
}

int8_t cRtcTimeGet(TimeType *ptypeTime)
{
    rtc_parameter_struct rtc_initpara;

    rtc_current_time_get(&rtc_initpara);

    ptypeTime->year     = ((rtc_initpara.year   / 16) * 10) + (rtc_initpara.year   % 16) + 2000;
    ptypeTime->month    = ((rtc_initpara.month  / 16) * 10) + (rtc_initpara.month  % 16);
    ptypeTime->day      = ((rtc_initpara.date   / 16) * 10) + (rtc_initpara.date   % 16);
    ptypeTime->hour     = ((rtc_initpara.hour   / 16) * 10) + (rtc_initpara.hour   % 16);
    ptypeTime->hour    += ((rtc_initpara.display_format == RTC_12HOUR) && (rtc_initpara.am_pm == RTC_PM)) ? 12 : 0;
    ptypeTime->minute   = ((rtc_initpara.minute / 16) * 10) + (rtc_initpara.minute % 16);
    ptypeTime->second   = ((rtc_initpara.second / 16) * 10) + (rtc_initpara.second % 16);
    ptypeTime->week     = rtc_initpara.day_of_week;

    return 0;
}

/* 设置RTC时间 */
void vRtcTimestampSet(uint32_t uiTimestamp)
{
    TimeType typeTime;

    vStampToTime(&typeTime, uiTimestamp, 8.0f);

    cRtcTimeSet(&typeTime);
}

/* 获取RTC时间 */
uint32_t uiRtcTimestampGet(void)
{
    TimeType typeTime;

    cRtcTimeGet(&typeTime);

    return lTimeToStamp(&typeTime);
}

/* 设置闹钟时刻 */
void vRtcAlarmSet(uint32_t uiTimestamp)
{
}

/* 设置RTC时区 */
void vRtcUTCSet(int8_t cUTC)
{
    cRtcBkpWrite(4, cUTC);
    cRtcBkpSave();
}

/* 获取RTC时区 */
int8_t cRtcUTCGet(void)
{
    /* 存储时区数据 */
    return (int8_t)ucRtcBkpRead(13);
}


/* 设置BKP区域的值 */
int8_t cRtcBkpWrite(int8_t cChannel, uint8_t ucValue)
{
    uint32_t uiData = REG32((RTC) + 0x00000050U + (cChannel & ~3));

    if((cChannel < 0) || (cChannel > 19))
        return 1;

    uiData = (uiData & ~(0xFF << ((cChannel & 3) * 8))) | ((uint32_t)ucValue << ((cChannel & 3) * 8));

    REG32((RTC) + 0x00000050U + (cChannel & ~3)) = uiData;

    return 0;
}

/* 获取BKP区域的值 */
uint8_t ucRtcBkpRead(int8_t cChannel)
{
    if((cChannel < 0) || (cChannel > 19))
        return 0;

    return (REG32((RTC) + 0x00000050U + (cChannel & ~3))) >> ((cChannel & 3) * 8);
}

int8_t cRtcBkpSave(void)
{
    uint16_t usCrc;
    uint8_t ucDatas[18], i;

    for(i = 0; i < sizeof(ucDatas); ++i)
    {
        ucDatas[i] = ucRtcBkpRead(i);
    }

    usCrc = usCRC16_MODBUS(NULL, ucDatas, sizeof(ucDatas));

    cRtcBkpWrite(18, usCrc);
    cRtcBkpWrite(19, usCrc >> 8);

    return 0;
}

int8_t cRtcBkpCheck(void)
{
    uint16_t usCrc;
    uint8_t ucDatas[20], i;

    for(i = 0; i < sizeof(ucDatas); ++i)
    {
        ucDatas[i] = ucRtcBkpRead(i);
    }

    usCrc = usCRC16_MODBUS(NULL, ucDatas, sizeof(ucDatas) - 2);
    if(usCrc != (ucDatas[18] + (ucDatas[19] << 8)))
        return 1;

    return 0;
}
